home *** CD-ROM | disk | FTP | other *** search
/ BMUG Revelations / BMUG Revelations.toast / Programming / Programming Languages / XLisp 2.1e3 / Sources / macint.c < prev    next >
Text File  |  1993-11-13  |  30KB  |  983 lines

  1. /* macint.c - macintosh interface routines for xlisp 2.1e */
  2. /* Written by Brian Kendig. */
  3. /* The functions here are only called by macstuff.c. */
  4.  
  5. #include <THINK.h>
  6. #include <Desk.h>
  7. #include <Events.h>
  8. #include <GestaltEQU.h>
  9. #include <LoMem.h>
  10. #include <Memory.h>
  11. #include <Menus.h>
  12. #include <OSEvents.h>
  13. #include <Quickdraw.h>
  14. #include <StandardFile.h>
  15. #include <TextEdit.h>
  16. #include <ToolUtils.h>
  17. #include <Traps.h>
  18. #include <Windows.h>
  19. #include <pascal.h>
  20. #include "macint.h"
  21.  
  22. #define TEXTREC        (*hTERec)  /* the command window text record */
  23. #define TEXTCHAR(i)    ((*(TEXTREC->hText))[i])
  24.  
  25. MenuHandle appleMenu, fileMenu, editMenu, controlMenu;
  26.  
  27. /* command and graphics windows */
  28. WindowPtr        commandWin, graphicsWin;
  29. WindowRecord    commandWinRec, bwGraphicsWinRec;
  30. CWindowRecord    colorGraphicsWinRec;
  31. Boolean            graphicsShown, commandWinResized = false;
  32. Rect            dragRect, sizeRect;
  33.  
  34. int screenWidth, screenHeight;                    /* screen dimensions */
  35. int sHorizontal, sVertical, sWidth, sHeight;    /* command win, split screen */
  36. int gHorizontal, gVertical, gWidth, gHeight;    /* graphics win, split screen */
  37.  
  38. /* output is buffered */
  39. Handle hOutputBuffer = NULL;
  40. int outputBufferLength = 0;
  41.  
  42. /* miscellaneous stuff */
  43. char        recentChar;                            /* the last character typed */
  44. int         cursorPos;                            /* the cursor's position on the line */
  45. short        linesInView;                        /* how many lines are in the window */
  46. CharsHandle    pastedTextH = NULL;                    /* a handle to pasted text */
  47. int            pastedLength = 0;                    /* how many chars there are in the paste buffer */
  48. int            flashTime = 0, cursorBeforeFlash;    /* for flashing cursor when parens match */
  49. int            cmdStart;                            /* where (in text record) the current command starts */
  50.  
  51. TextStyle textStyle[2];  /* styles: bold for user input, plain for output */
  52. enum { plainStyle, boldStyle } currentStyle;
  53.  
  54. int wneImplemented;
  55. EventRecord theEvent;
  56. TEHandle hTERec;
  57. ControlHandle vScroll;
  58. unsigned long startupTicks;
  59. Boolean hasColorQD;
  60.  
  61. short howManyFiles = 0, whichFile = 0;  /* keep track of files opened from Finder */
  62.  
  63. void DoEvent (void);        /* forward */
  64. void FlushOutput (void);    /* forward */
  65.  
  66. static void DoScrollBar (ControlHandle control, short change) {
  67.     /* keep track of the user as he fiddles with the scroll bar */
  68.     /* This routine is called while the user has the mouse down. */
  69.     /* It makes sure the thumb isn't dragged out-of-bounds. */
  70.     short value = GetCtlValue (control), max = GetCtlMax (control);
  71.     short newval = value + change;
  72.     if (newval < 0) newval = 0; else if (newval > max) newval = max;
  73.     SetCtlValue (control, newval);
  74.     if (newval != value) TEScroll (0, (value - newval) * LINEHEIGHT, hTERec);
  75. }
  76.  
  77. static pascal void ScrollProc (ControlHandle control, short thePart) {
  78.     /* for clicks in the scroll bar or arrows; update the window properly */
  79.     short amount, linesInView;
  80.     WindowPtr window;
  81.  
  82.     if (!thePart) return;
  83.     window = (*control)->contrlOwner;
  84.     switch (thePart) {
  85.         case inUpButton:    amount = -1;                break;
  86.         case inDownButton:    amount = 1;                    break;
  87.         case inPageUp:        amount = -linesInView;        break;
  88.         case inPageDown:    amount = linesInView;        break;
  89.     }
  90.     DoScrollBar (control, amount);
  91. }
  92.  
  93. static pascal Boolean ScrollClikLoop (void) {
  94.     Rect tempRect;
  95.     Point mouse;
  96.     GrafPtr oldPort;
  97.     RgnHandle oldClip;
  98.     short amount = 0;
  99.  
  100.     if (FrontWindow () != commandWin) return false;
  101.  
  102.     GetPort (&oldPort);
  103.     SetPort (commandWin);
  104.     GetClip (oldClip = NewRgn ());
  105.     SetRect (&tempRect, INT_MIN, INT_MIN, INT_MAX, INT_MAX);
  106.     ClipRect (&tempRect);
  107.  
  108.     GetMouse (&mouse);
  109.     if (mouse.v < TEXTREC->viewRect.top)              DoScrollBar (vScroll, -1);
  110.     else if (mouse.v > TEXTREC->viewRect.bottom)    DoScrollBar (vScroll, 1);
  111.  
  112.     SetClip (oldClip);
  113.     DisposeRgn (oldClip);
  114.     SetPort (oldPort);
  115.     return true;
  116. }
  117.  
  118. static Rect SetTERect (void) {
  119.     /* set the dimensions of the text record in its window */
  120.     Rect teRect = commandWin->portRect;
  121.     teRect.right -= SCROLLER_WIDTH;
  122.     InsetRect (&teRect, TEXT_MARGIN, TEXT_MARGIN);
  123.     linesInView = (teRect.bottom - teRect.top) / LINEHEIGHT;
  124.     teRect.bottom = teRect.top + linesInView * LINEHEIGHT;  /* round off */
  125.     return teRect;
  126. }
  127.  
  128. static void SetScrollRect (void) {
  129.     /* set the dimensions of the scroll bar in its window */
  130.     MoveControl (vScroll, commandWin->portRect.right - SCROLLER_WIDTH, -1);
  131.     SizeControl (vScroll, SCROLLER_WIDTH + 1,
  132.             (commandWin->portRect.bottom - commandWin->portRect.top) - (SCROLLER_WIDTH - 2));
  133.  
  134. }
  135.  
  136. static void AdjustCursor (void) {
  137.     /* make the pointer an I-beam iff it's in the text window */
  138.     Point mouse = theEvent.where;
  139.     if (commandWin != FrontWindow ()) {
  140.         SetCursor (&arrow);
  141.         return;
  142.     } else {
  143.         GlobalToLocal (&mouse);
  144.         if (PtInRect (mouse, &(TEXTREC->viewRect))) SetCursor (*GetCursor (iBeamCursor));
  145.         else SetCursor (&arrow);
  146.     }
  147. }
  148.  
  149. void ShowGrafWin (void) {
  150.     /* make the graphics window visible */
  151.     ShowWindow (graphicsWin);
  152.     SelectWindow (graphicsWin);
  153.     SetItem (controlMenu, SHOW_GRAPHICS, "\pHide Graphics");
  154.     AdjustCursor ();
  155.     graphicsShown = true;
  156. }
  157.  
  158. void HideGrafWin (void) {
  159.     /* hide the graphics window */
  160.     HideWindow (graphicsWin);
  161.     SetItem (controlMenu, SHOW_GRAPHICS, "\pShow Graphics");
  162.     graphicsShown = false;
  163. }
  164.  
  165. static short roundup (float x) {  /* a kludge to round up a float to an int */
  166.     if (((int) x) != ((int) (x += 0.5))) x += 0.5;
  167.     return (int) x;
  168. }
  169.  
  170. static void AdjustScrollBar (void) {
  171.     /* adjust the scroll bar to match the position of the text view */
  172.     short oldval = GetCtlValue (vScroll), oldmax = GetCtlMax (vScroll);
  173.     short value, max;
  174.  
  175.     max = TEXTREC->nLines - linesInView;
  176.     if ((TEXTREC->teLength > 0) && (*(*TEXTREC->hText + TEXTREC->teLength - 1) == '\r')) max++;
  177.     if (max < 0) max = 0;
  178.     if (max != oldmax) SetCtlMax (vScroll, max);
  179.     value = roundup ((TEXTREC->viewRect.top - TEXTREC->destRect.top) / LINEHEIGHT);
  180.     if (value < 0) value = 0; else if (value > max) value = max;
  181.     if (value != oldval) SetCtlValue (vScroll, value);
  182. }
  183.  
  184. static void DrawOnlyGrowIcon (WindowPtr window) {  /* doesn't draw scroll bars */
  185.     RgnHandle saveRgn;
  186.     Rect growRect;
  187.  
  188.     growRect = window->portRect;
  189.     growRect.top = growRect.bottom - SCROLLER_WIDTH;
  190.     growRect.left = growRect.right - SCROLLER_WIDTH;
  191.     GetClip (saveRgn = NewRgn ());
  192.     ClipRect (&growRect);
  193.     DrawGrowIcon (window);
  194.     SetClip (saveRgn);
  195.     DisposeRgn (saveRgn);
  196. }
  197.  
  198. static void UpdateCmdWindow (void) {
  199.     long textBottom;
  200.  
  201.     InvalRect (&(commandWin->portRect));
  202.     BeginUpdate (commandWin);
  203.     EraseRect (&(commandWin->portRect));
  204.     FlushOutput ();
  205.     DrawOnlyGrowIcon (commandWin);
  206.     if (commandWinResized) {
  207.         TEXTREC->viewRect = SetTERect ();
  208.         TEXTREC->destRect.right = TEXTREC->viewRect.right;
  209.         TECalText (hTERec);
  210.         SetScrollRect ();
  211.         commandWinResized = false;
  212.     }
  213.  
  214.     TEXTREC->viewRect = SetTERect ();  /* adjust for possible change in height of status line */
  215.  
  216.     textBottom = TEXTREC->destRect.top + (TEXTREC->nLines * LINEHEIGHT);
  217.     if (TEXTREC->destRect.top > TEXTREC->viewRect.top)
  218.         TEScroll (0, (TEXTREC->viewRect.top - TEXTREC->destRect.top), hTERec);
  219.  
  220.     if (TEXTREC->destRect.top < TEXTREC->viewRect.top) {  /* make sure we don't get fractions of lineheights */
  221.         int amountOffTheTop = TEXTREC->viewRect.top - TEXTREC->destRect.top;
  222.         if (amountOffTheTop % LINEHEIGHT) TEScroll (0, amountOffTheTop % LINEHEIGHT, hTERec);
  223.     }
  224.     TEUpdate (&(TEXTREC->viewRect), hTERec);
  225.     AdjustScrollBar ();
  226.     UpdtControl (commandWin, commandWin->visRgn);
  227.     EndUpdate (commandWin);
  228. }
  229.  
  230. void InitMac (void) {
  231.     /* do all the necessary initialization mumbo-jumbo */
  232.  
  233.     {  /* set up memory properly */
  234.         int i;
  235.         if (DefltStack < STACKMIN) SetApplLimit (CurStackBase - STACKMIN);
  236.         MaxApplZone ();
  237.         for (i = 0; i < MASTERS; i++) MoreMasters ();
  238.     }
  239.  
  240.     /* initialize the toolbox */
  241.     InitGraf (&thePort);
  242.     InitFonts ();
  243.     FlushEvents (everyEvent, 0);
  244.     InitWindows ();
  245.     InitMenus ();
  246.     TEInit ();
  247.     InitDialogs (NIL);
  248.     InitCursor ();
  249.  
  250.     /* see if we have WaitNextEvent and Color Quickdraw */
  251.     wneImplemented = (NGetTrapAddress (_WaitNextEvent, ToolTrap) != NGetTrapAddress (_Unimplemented, ToolTrap));
  252.     if (NGetTrapAddress ((short) Gestalt, ToolTrap) != NGetTrapAddress (_Unimplemented, ToolTrap)) {
  253.         long returnCode;
  254.         OSErr err = Gestalt (gestaltQuickdrawVersion, &returnCode);
  255.         hasColorQD = ((err == noErr) && (returnCode >= gestalt8BitQD));
  256.     } else hasColorQD = false;
  257.  
  258.     {  /* set up menus */
  259.         Handle theMenuBar = GetNewMBar (MBAR_RES);
  260.         SetMenuBar (theMenuBar);
  261.         appleMenu = GetMHandle (APPLE_MENU_RES);
  262.         fileMenu = GetMHandle (FILE_MENU_RES);
  263.         editMenu = GetMHandle (EDIT_MENU_RES);
  264.         controlMenu = GetMHandle (CONTROL_MENU_RES);
  265.         AddResMenu (appleMenu, 'DRVR');
  266.         DrawMenuBar ();
  267.     }
  268.  
  269.     /* get the size of the main screen */
  270.     screenWidth  = screenBits.bounds.right  - screenBits.bounds.left;
  271.     screenHeight = screenBits.bounds.bottom - screenBits.bounds.top;
  272.  
  273.     /* create the command and graphics windows */
  274.     commandWin = GetNewWindow (CWINRES, &commandWinRec, (WindowPtr) -1L);
  275.     if (hasColorQD) graphicsWin = GetNewCWindow (GWINRES, &colorGraphicsWinRec, (WindowPtr) -1L);
  276.     else graphicsWin = GetNewWindow (GWINRES, &bwGraphicsWinRec, (WindowPtr) -1L);
  277.     SetPort (commandWin);
  278.  
  279.     /* compute the size of the graphics window in split-screen mode */
  280.     gHorizontal = SCREEN_MARGIN;
  281.     gVertical = MBAR_HEIGHT + TITLEBAR_HEIGHT - 1;
  282.     gWidth = screenWidth - (SCREEN_MARGIN * 2);
  283.     gHeight = GRAFWIN_HEIGHT;
  284.  
  285.     /* compute the size of the command window in split-screen mode */
  286.     sHorizontal = SCREEN_MARGIN;
  287.     sVertical = MBAR_HEIGHT + TITLEBAR_HEIGHT - 1 + SCREEN_MARGIN + GRAFWIN_HEIGHT;
  288.     sWidth = screenWidth - (SCREEN_MARGIN * 2);
  289.     sHeight = screenHeight - MBAR_HEIGHT - TITLEBAR_HEIGHT - (SCREEN_MARGIN * 2) - GRAFWIN_HEIGHT - 1;
  290.  
  291.     /* set up size and drag rects */
  292.     dragRect = (*GetGrayRgn ())->rgnBBox;
  293.     dragRect.left += DRAG_THRESHOLD;
  294.     dragRect.right -= DRAG_THRESHOLD;
  295.     dragRect.bottom -= DRAG_THRESHOLD;
  296.     sizeRect.top = MIN_WIN_HEIGHT;
  297.     sizeRect.left = MIN_WIN_WIDTH;
  298.     sizeRect.bottom = screenBits.bounds.bottom - screenBits.bounds.top;
  299.     sizeRect.right = screenBits.bounds.right - screenBits.bounds.left;
  300.  
  301.     /* setup the font, size and writing mode for the command window */
  302.     TextFont (monaco);
  303.     TextSize (9);
  304.     TextFace (0);
  305.     TextMode (srcCopy);
  306.     textStyle[plainStyle].tsFace = 0;
  307.     textStyle[boldStyle].tsFace = bold;
  308.     currentStyle = plainStyle;
  309.  
  310.     startupTicks = TickCount ();  /* take note of what time we're starting up */
  311.  
  312.     {  /* set up scroll bar */
  313.         Rect scrollRect;
  314.         vScroll = NewControl (commandWin, &scrollRect, "\p", 0, 0, 0, 0, scrollBarProc, NIL);
  315.         SetScrollRect ();
  316.         ShowControl (vScroll);
  317.     }
  318.  
  319.     {  /* set up command text record */
  320.         Rect teRect = SetTERect ();
  321.         hTERec = TEStylNew (&teRect, &teRect);
  322.          TECalText (hTERec);
  323.         TEAutoView (true, hTERec);
  324.         SetClikLoop (ScrollClikLoop, hTERec);
  325.         TEActivate (hTERec);
  326.     }
  327.  
  328.     hOutputBuffer = NewHandle (MAX_BUF);  /* a handle to a buffer for text to be displayed */
  329.  
  330.     HideGrafWin ();
  331.  
  332.     {  /* see if the user launched the app by opening text files from the Finder */
  333.         short doWhat;
  334.         CountAppFiles (&doWhat, &howManyFiles);
  335.         if (doWhat != appOpen) howManyFiles = 0;
  336.     }
  337.  
  338.     UpdateCmdWindow ();
  339.  
  340. }
  341.  
  342. static void SetSelection (short start, short end) {
  343.     TEXTREC->clikStuff = 255;  /* to make sure the caret appears at the start of a line when it should */
  344.     /* see tech note "TextEdit EOL Ambiguity" for more information */
  345.     TESetSelect (start, end, hTERec);
  346. }
  347.  
  348. static void CancelFlash (void) {
  349.     /* cancel the matching-paren flashing */
  350.     if (flashTime) {
  351.         flashTime = 0;
  352.         SetSelection (cursorBeforeFlash, cursorBeforeFlash);
  353.     }
  354. }
  355.  
  356. static void StopPasting (void) {
  357.     /* clean up after finishing a paste */
  358.     pastedLength = 0;
  359.     if (pastedTextH) {
  360.         DisposHandle (pastedTextH);
  361.         pastedTextH = NULL;
  362.     }
  363. }
  364.  
  365. static void DoStyle (int whatStyle) {
  366.     /* set the text to a certain style */
  367.     TESetStyle (doFace, &(textStyle[whatStyle]), false, hTERec);
  368. }
  369.  
  370. static void FlushOutput (void) {
  371.     /* clear out the output buffer, dumping its contents to the window */
  372.     short totalLines, scrollAmount, max;
  373.  
  374.     if (outputBufferLength == 0) return;
  375.     CancelFlash ();
  376.     DoStyle (plainStyle);
  377.     HLock (hOutputBuffer);
  378.     TEInsert (*hOutputBuffer, outputBufferLength, hTERec);
  379.     HUnlock (hOutputBuffer);
  380.     outputBufferLength = 0;
  381.  
  382.     if (TEXTREC->teLength > SCROLLBACK_THRESHHOLD) {  /* make sure TE record isn't too long */
  383.         int i = 1, newLength;
  384.         TEPtr textPtr;
  385.         while ((TEXTREC->teLength - TEXTREC->lineStarts[i]) > (SCROLLBACK_THRESHHOLD - DELETE_BLOCK)) i++;
  386.         i = TEXTREC->lineStarts[i];
  387.         newLength = TEXTREC->teLength - i;
  388.         textPtr = (TEPtr)(*(TEXTREC->hText));
  389.         BlockMove ((Ptr)((long)textPtr + i), textPtr, newLength);
  390.         SetHandleSize (TEXTREC->hText, newLength);
  391.         TEXTREC->destRect.top += LINEHEIGHT;
  392.         TECalText (hTERec);
  393.         TEUpdate (&(TEXTREC->viewRect), hTERec);
  394.     }
  395.     TESelView (hTERec);
  396.     AdjustScrollBar ();
  397. }
  398.  
  399. static void PrepareForInput (void) {
  400.     /* get ready to take input */
  401.     FlushOutput ();
  402.     cmdStart = TEXTREC->selStart;
  403. }
  404.  
  405. static void DeleteRange (void) {
  406.     /* delete the selected range of text, updating cmdStart as necessary */
  407.     if (TEXTREC->selEnd <= cmdStart) return;
  408.     if (TEXTREC->selStart < cmdStart) SetSelection (cmdStart, TEXTREC->selEnd);
  409.     TEDelete (hTERec);
  410. }
  411.  
  412. static void CopyThisLineToEnd (void) {
  413.     /* copy the line the caret is on to the end */
  414.     char *buffer;
  415.     short b, i, caretOffset;
  416.  
  417.     /* first find out exactly where it starts */
  418.     i = TEXTREC->nLines-1;  /* first find which line */
  419.     while (TEXTREC->selStart < TEXTREC->lineStarts[i]) i--;
  420.     while ((i > 0) && ((*(TEXTREC->hText))[TEXTREC->lineStarts[i]-1] != '\r'))
  421.         i--;  /* for wrapped lines */
  422.     i = TEXTREC->lineStarts[i];  /* now zero in on the exact character where it begins */
  423.     while ((TEXTCHAR(i) >= '0') && (TEXTCHAR(i) <= '9')) i++;  /* skip error level */
  424.     if ((TEXTCHAR(i) == '>') && (TEXTCHAR(i+1) == ' ')) i+=2;  /* get rid of leading prompt */
  425.  
  426.     caretOffset = TEXTREC->selStart - i;  /* how many characters in is the caret? */
  427.  
  428.     /* now put the line into the buffer */
  429.     b = 0;
  430.     while ((TEXTCHAR(i+b) != '\r') && (i+b < TEXTREC->teLength)) b++;  /* find the end of the line */
  431.     buffer = (char *) NewPtr (b);
  432.     BlockMove (*TEXTREC->hText + i, buffer, b);
  433.     buffer[b] = '\0';
  434.  
  435.     /* delete whatever's already on the last line */
  436.     SetSelection (cmdStart, TEXTREC->teLength);
  437.     TEDelete (hTERec);
  438.  
  439.     DoStyle (boldStyle);
  440.     TEInsert (buffer, b, hTERec);
  441.     DisposPtr (buffer);
  442.  
  443.     if (caretOffset < 0) caretOffset = b;
  444.     SetSelection (cmdStart + caretOffset, cmdStart + caretOffset);
  445. }
  446.  
  447. static void GoStartOfLine (void) {
  448.     short whichLine = TEXTREC->nLines - 1;  /* look for the caret; start at the end and go up */
  449.     while (TEXTREC->lineStarts[whichLine] > TEXTREC->selStart) whichLine--;
  450.     SetSelection (TEXTREC->lineStarts[whichLine], TEXTREC->lineStarts[whichLine]);
  451.     AdjustScrollBar ();
  452. }
  453.  
  454. static void GoEndOfLine (void) {
  455.     short whichLine = TEXTREC->nLines - 1;  /* look for the caret; start at the end and go up */
  456.     while (TEXTREC->lineStarts[whichLine] > TEXTREC->selStart) whichLine--;
  457.     if (whichLine == TEXTREC->nLines - 1)
  458.         SetSelection (TEXTREC->teLength, TEXTREC->teLength);
  459.     else SetSelection (TEXTREC->lineStarts[whichLine+1] - 1, TEXTREC->lineStarts[whichLine+1] - 1);
  460.     AdjustScrollBar ();
  461. }
  462.  
  463. static void GoBackOneWord (void) {
  464.     short i = TEXTREC->selStart;
  465.     while ((i > 0) && !isalnum (TEXTCHAR(i-1)))    i--;
  466.     while ((i > 0) && isalnum (TEXTCHAR(i-1)))    i--;
  467.     SetSelection (i, i);
  468. }
  469.  
  470. static void GoForwardOneWord (void) {
  471.     short i = TEXTREC->selStart;
  472.     while ((i < TEXTREC->teLength) && !isalnum (TEXTCHAR(i)))    i++;
  473.     while ((i < TEXTREC->teLength) && isalnum (TEXTCHAR(i)))    i++;
  474.     SetSelection (i, i);
  475. }
  476.  
  477. static void EditFreely (void) {
  478.     Boolean done;
  479.     do {
  480.         done = false;
  481.         DoEvent ();
  482.         if (pastedLength > 0) {  /* if there is still text to paste, paste it */
  483.             int i = 0;
  484.             CancelFlash ();
  485.             if (TEXTREC->selStart < cmdStart) StopPasting ();
  486.             else {
  487.                 while ((i < pastedLength) && (((char *)(*pastedTextH))[i] != '\r')) i++;
  488.                 DoStyle (boldStyle);
  489.                 TEInsert (*pastedTextH, i, hTERec);
  490.                 AdjustScrollBar ();
  491.                 if (i < pastedLength) {  /* we were stopped by a carriage return, so eat it */
  492.                     i++;
  493.                     done = true;
  494.                 }
  495.                 pastedLength -= i;
  496.                 if (pastedLength > 0) {
  497.                     BlockMove ((Ptr)((long)(*pastedTextH) + i), *pastedTextH, pastedLength);
  498.                     SetHandleSize (pastedTextH, pastedLength);
  499.                 } else StopPasting ();
  500.             }
  501.         }
  502.         else if (recentChar) {  /* if the last event got us a character, process it */
  503.             int i;
  504.             Boolean wasOnLastLine;
  505.             CancelFlash ();
  506.  
  507.             if ((TEXTREC->selEnd <= cmdStart) && (TEXTREC->selStart != TEXTREC->selEnd)) continue;
  508.             if (TEXTREC->selStart < cmdStart) SetSelection (cmdStart, TEXTREC->selEnd);
  509.             wasOnLastLine = (TEXTREC->selStart >= cmdStart);
  510.  
  511.             if ((recentChar & 0xfc) == 0x1c) {  /* was this an arrow key? */
  512.                 TEXTREC->clikStuff = 255;  /* to make sure the caret appears where it should */
  513.                 TEKey (recentChar, hTERec);
  514.                 AdjustScrollBar ();
  515.                 continue;
  516.             }
  517.             if (!wasOnLastLine) CopyThisLineToEnd ();
  518.             switch (recentChar) {
  519.             case FWDDEL:
  520.                 if (TEXTREC->selStart != TEXTREC->selEnd) DeleteRange ();
  521.                 else if ((TEXTREC->selStart >= cmdStart) && (TEXTREC->selStart < TEXTREC->teLength)) {
  522.                     TEDeactivate (hTERec);
  523.                     SetSelection (TEXTREC->selStart, TEXTREC->selStart + 1);
  524.                     TEDelete (hTERec);
  525.                     if (FrontWindow () == commandWin) TEActivate (hTERec);
  526.                 }
  527.                 break;
  528.             case CLRKEY:
  529.                 if (TEXTREC->selStart != TEXTREC->selEnd) DeleteRange ();
  530.                 break;
  531.             case DELETE:
  532.                 if (TEXTREC->selStart != TEXTREC->selEnd) DeleteRange ();
  533.                 else if (TEXTREC->selStart > cmdStart) {
  534.                     TEXTREC->clikStuff = 255;  /* to make sure the caret appears where it should */
  535.                     TEKey (DELETE, hTERec);
  536.                 }
  537.                 break;
  538.             case RETURN:
  539.                 if (wasOnLastLine) done = true;
  540.                 break;
  541.             case ENTER:  /* ENTER ends command no matter what */
  542.                 done = true;
  543.                 break;
  544.             default:
  545.                 DoStyle (boldStyle);
  546.                 TEXTREC->clikStuff = 255;  /* to make sure the caret appears where it should */
  547.                 TEKey (recentChar, hTERec);
  548.                 if ((recentChar == ')') && (TEXTREC->selStart > cmdStart)) {
  549.                     short parenCount = -1;
  550.                     Boolean inQuotes = false;
  551.                     i = TEXTREC->selStart - 1;
  552.                     while ((--i >= cmdStart) && (parenCount != 0))
  553.                         switch ((*TEXTREC->hText)[i]) {
  554.                         case DBLQUOTE: inQuotes = !inQuotes; break;
  555.                         case '(': if (!inQuotes) parenCount++; break;
  556.                         case ')': if (!inQuotes) parenCount--; break;
  557.                         }
  558.                     if (parenCount == 0) {
  559.                         cursorBeforeFlash = TEXTREC->selStart;
  560.                         SetSelection (i+1, i+2);  /* flash the matching open-paren */
  561.                         flashTime = 100;
  562.                     }
  563.                 } else if ((recentChar == DBLQUOTE) && (TEXTREC->selStart > cmdStart)) {
  564.                     i = TEXTREC->selStart - 1;
  565.                     while ((--i >= cmdStart) && ((*TEXTREC->hText)[i] != DBLQUOTE)) ;
  566.                     if ((*TEXTREC->hText)[i] == DBLQUOTE) {
  567.                         cursorBeforeFlash = TEXTREC->selStart;
  568.                         SetSelection (i, i+1);  /* flash the matching double-quote */
  569.                         flashTime = 100;
  570.                     }
  571.                 }
  572.             }
  573.             AdjustScrollBar ();
  574.         }
  575.     } while (!done);
  576. }
  577.  
  578. char *macgets (void) {
  579.     /* retrieve a typed character */
  580.     /* Note that this uses some extensive (and clever, if I may say so myself) buffering. */
  581.     int i, b, bufSize;
  582.     char *ptr, *buffer;
  583.     Boolean done, onLastLine;
  584.     
  585.     PrepareForInput ();
  586.     do {  /* repeat until a full expression has been typed */
  587.         EditFreely ();  /* allow free editing for a while */
  588.  
  589.         /* Now, we have a complete command to parse, if and only if: */
  590.         /* - the cursor was on the last line when the user pressed Return or Enter, and */
  591.         /* - the user either pressed Enter, or else every '(' since the beginning */
  592.         /*     of the command is matched by a ')'. */
  593.         /* Quoting is watched for.  ( ") is not a complete expression. */
  594.  
  595.         done = true;
  596.         if (TEXTREC->selStart != TEXTREC->teLength)  /* if we're not at the end already */
  597.             SetSelection (TEXTREC->teLength, TEXTREC->teLength);  /* send cursor to end */
  598.         TEXTREC->clikStuff = 255;  /* to make sure the caret appears where it should */
  599.         TEKey ('\r', hTERec);
  600.  
  601.         /* check and see if we've completed the command yet */
  602.         if (recentChar != ENTER) {
  603.             Boolean inQuotes = false;
  604.             short parenCount = 0;
  605.             for (i = cmdStart; i < TEXTREC->teLength; i++)
  606.                 switch ((*TEXTREC->hText)[i]) {
  607.                 case DBLQUOTE: inQuotes = !inQuotes; break;
  608.                 case '(': if (!inQuotes) parenCount++; break;
  609.                 case ')': if (!inQuotes) parenCount--; break;
  610.                 }
  611.             if ((parenCount > 0) || inQuotes) done = false;
  612.         }
  613.  
  614.         AdjustScrollBar ();
  615.     } while (!done);
  616.  
  617.     /* put the entire command into the buffer, and return it */
  618.     bufSize = TEXTREC->teLength - cmdStart;
  619.     buffer = (char *) NewPtr (bufSize + 1);
  620.     BlockMove (*TEXTREC->hText + cmdStart, buffer, bufSize);
  621.     buffer[bufSize] = '\0';
  622.     return buffer;
  623. }
  624.  
  625. void macputc (int ch) {
  626.     /* put a char into the output buffer, and flush the buffer if necessary */
  627.     switch (ch) {
  628.         case '\t':
  629.             do { macputc (' '); } while (cursorPos & 7);
  630.             break;
  631.         case DELETE:
  632.             if (cursorPos) cursorPos--;  /* and fall through to default */
  633.         default:
  634.             if (outputBufferLength == MAX_BUF) FlushOutput ();
  635.             if (ch == '\n') {
  636.                 cursorPos = 0;
  637.                 (*hOutputBuffer)[outputBufferLength++] = '\r';
  638.             } else {
  639.                 cursorPos++;
  640.                 (*hOutputBuffer)[outputBufferLength++] = ch;
  641.             }
  642.     }
  643. }
  644.  
  645. void macputs (char *s) {
  646.     /* for completeness */
  647.     while (*s) macputc (*s++);
  648. }
  649.  
  650. void scrflush (void) {
  651.     extern void osflush (void);
  652.     /* clear out everything */
  653.     FlushOutput ();
  654.     osflush ();
  655. }
  656.  
  657. void scrclear (void) {
  658.     /* clear text window -- not implemented */
  659. }
  660.  
  661. static void DoAppleMenu (int theItem) {
  662.     switch (theItem) {
  663.         case ABOUT_ITEM:
  664.             DoAboutBox ();
  665. /***            NoteAlert (ABOUT_BOX, NIL); */
  666.             break;
  667.         default: {
  668.             Str255 name;
  669.             GetItem (appleMenu, theItem, name);
  670.             OpenDeskAcc (name);
  671.             break;
  672.         }
  673.     }
  674. }
  675.  
  676. static void DoFileMenu (int theItem) {
  677.     extern xlload (char *, int, int);
  678.     extern wrapup (void);
  679.     SFReply theFile;
  680.     SFTypeList fileTypes;
  681.     Point pt = { 100, 100 };
  682.  
  683.     fileTypes[0] = 'TEXT';
  684.     switch (theItem) {
  685.         case LOAD:
  686.         case LOAD_NOISILY:
  687.             StopPasting ();
  688.             SFGetFile (pt, NIL, NIL, 1, fileTypes, NIL, &theFile);
  689. /*            EraseBottomScroller (true);  /* SFGetFile puts it there */
  690.             if (theFile.good) {
  691.                 Str255 theFullPath;
  692.                 HiliteMenu (0);
  693.                 SetVol (NIL, theFile.vRefNum);
  694.                 SetSelection (TEXTREC->teLength, TEXTREC->teLength);  /* send cursor to end */
  695.                 if ((xlload (PtoCstr (theFile.fName), 1, (theItem == LOAD_NOISILY))) == 0)
  696.                     xlabort ("load error");
  697.                 macputs ("> ");
  698.                 PrepareForInput ();
  699.             }
  700.             break;
  701.         case QUIT:
  702.             wrapup ();
  703.     }
  704. }
  705.  
  706. static void DoEditMenu (int theItem) {
  707.     if (SystemEdit (theItem-1) == false)
  708.         switch (theItem) {
  709.         case CUT: case COPY:
  710.             if (ZeroScrap () == noErr) {
  711.                 TECopy (hTERec);  /* after copying, export the TE scrap */
  712.                 if (TEToScrap () != noErr) ZeroScrap ();
  713.             }
  714.             if (theItem == CUT) DeleteRange ();
  715.             break;
  716.         case PASTE: {
  717.             long scrapOffset;
  718.             if (pastedTextH) DisposHandle (pastedTextH);
  719.             pastedTextH = (CharsHandle) NewHandle (0);
  720.             pastedLength = GetScrap (pastedTextH, 'TEXT', &scrapOffset);
  721.             if (pastedLength < 0) pastedLength = 0;  /* error */
  722.             else {
  723.                 SetHandleSize (pastedTextH, pastedLength + 1);
  724.                 HLock (pastedTextH);
  725.                 ((char *)(*pastedTextH))[pastedLength] = '\0';
  726.                 HUnlock (pastedTextH);
  727.                 }
  728.             }  /* and fall through ... */
  729.         case CLEAR:
  730.             DeleteRange ();
  731.             break;
  732.         }
  733. }
  734.  
  735. static void DoControlMenu (int theItem) {
  736.     extern xlbreak (char *, char *);
  737.     extern char *s_unbound;
  738.     extern xlcontinue (void);
  739.     extern xlcleanup (void);
  740.     extern xlabort (char *);
  741.     extern xltoplevel (void);
  742.  
  743.     scrflush ();
  744.     HiliteMenu (0);
  745.     switch (theItem) {
  746.         case BREAK:            StopPasting ();    xlbreak ("user break", s_unbound);    PrepareForInput ();    break;
  747.         case CONTINUE:        StopPasting ();    xlcontinue ();                        PrepareForInput ();    break;
  748.         case CLEAN_UP:        StopPasting ();    xlcleanup ();                        PrepareForInput ();    break;
  749.         case CANCEL_INPUT:    StopPasting ();    xlabort ("input canceled");            PrepareForInput ();    break;
  750.         case TOP_LEVEL:        StopPasting ();    xltoplevel ();                        PrepareForInput ();    break;
  751.         case SHOW_GRAPHICS:
  752.             if (graphicsShown) HideGrafWin ();
  753.             else ShowGrafWin ();
  754.             break;
  755.         case SPLIT_SCREEN:
  756.             MoveWindow (commandWin, sHorizontal, sVertical, -1);
  757.             SizeWindow (commandWin, sWidth, sHeight, -1);
  758.             InvalRect (&commandWin->portRect);
  759.             SetTERect ();
  760.             SetScrollRect ();
  761.             ShowGrafWin ();
  762.             MoveWindow (graphicsWin, gHorizontal, gVertical, -1);
  763.             SizeWindow (graphicsWin, gWidth, gHeight, -1);
  764.             break;
  765.     }
  766. }
  767.  
  768. static void DoMenu (long choice) {
  769.     int theMenu = HiWord (choice), theItem = LoWord (choice);
  770.  
  771.     HiliteMenu (theMenu);
  772.     switch (theMenu) {
  773.         case APPLE_MENU_RES:    DoAppleMenu (theItem);        break;
  774.         case FILE_MENU_RES:        DoFileMenu (theItem);        break;
  775.         case EDIT_MENU_RES:        DoEditMenu (theItem);        break;
  776.         case CONTROL_MENU_RES:    DoControlMenu (theItem);    break;
  777.     }
  778.     HiliteMenu (0);
  779. }
  780.  
  781. static void AdjustMenus (void) {
  782.     /* turn the stuff in the Edit menu on and off as necessary */
  783.     long temp;
  784.     DisableItem (editMenu, UNDO);
  785.     if (TEXTREC->selStart != TEXTREC->selEnd) {
  786.         EnableItem (editMenu, CUT);
  787.         EnableItem (editMenu, COPY);
  788.         EnableItem (editMenu, CLEAR);
  789.     } else {
  790.         DisableItem (editMenu, CUT);
  791.         DisableItem (editMenu, COPY);
  792.         DisableItem (editMenu, CLEAR);
  793.     }
  794.     if (GetScrap (NIL, 'TEXT', &temp) > 0) EnableItem (editMenu, PASTE);
  795.     else DisableItem (editMenu, PASTE);
  796. }
  797.  
  798. static void DoContent (void) {
  799.     /* handle a click in a window's content region */
  800.     ControlHandle theScrollBar;
  801.     GrafPtr oldPort;
  802.     int scrollValue;
  803.     Point mouse = theEvent.where;
  804.     int thePart;
  805.  
  806.     GetPort (&oldPort);
  807.     SetPort (commandWin);
  808.     GlobalToLocal (&mouse);
  809.     
  810.     if (thePart = FindControl (mouse, commandWin, &theScrollBar))
  811.         switch (thePart) {
  812.             case inUpButton:
  813.             case inDownButton:
  814.             case inPageUp:
  815.             case inPageDown:
  816.                 scrollValue = TrackControl (theScrollBar, mouse, (ProcPtr) ScrollProc);
  817.                 break;
  818.             case inThumb:
  819.                 scrollValue = GetCtlValue (theScrollBar);
  820.                 thePart = TrackControl (theScrollBar, mouse, NIL);
  821.                 if (thePart) {
  822.                     scrollValue -= GetCtlValue (theScrollBar);
  823.                     if (scrollValue) TEScroll (0, scrollValue * LINEHEIGHT, hTERec);
  824.                 }
  825.                 break;
  826.         }
  827.  
  828.     else if (PtInRect (mouse, &(TEXTREC->viewRect)))
  829.         TEClick (mouse, (theEvent.modifiers & shiftKey) != 0, hTERec);
  830.  
  831.     SetPort (oldPort);
  832.  
  833. }
  834.  
  835. static void DoMouseDown (void) {
  836.     WindowPtr whichWindow;
  837.     short int thePart = FindWindow (theEvent.where, &whichWindow);
  838.  
  839.     switch (thePart) {
  840.         case inSysWindow:
  841.             SystemClick (&theEvent, whichWindow);
  842.             break;
  843.         case inDrag:
  844.             DragWindow (whichWindow, theEvent.where, &dragRect);
  845.             break;
  846.         case inMenuBar: {
  847.             long choice;
  848.             AdjustMenus ();
  849.             choice = MenuSelect (theEvent.where);
  850.             if (choice) DoMenu (choice);
  851.             break;
  852.         }
  853.         case inGoAway:
  854.             if ((whichWindow == graphicsWin)
  855.                     && (TrackGoAway (whichWindow, theEvent.where)))
  856.                 HideGrafWin ();
  857.             break;
  858.         case inContent:
  859.             if ((FrontWindow () == commandWin) && (whichWindow == commandWin))
  860.                 DoContent ();
  861.             else SelectWindow (whichWindow);
  862.             break;
  863.         case inGrow:
  864.         case inZoomIn:
  865.         case inZoomOut: {
  866.             long newSize;
  867.             GrafPtr oldPort;
  868.             if (thePart == inGrow) newSize = GrowWindow (whichWindow, theEvent.where, &sizeRect);
  869.             if (((thePart == inGrow) && newSize)
  870.                 || ((thePart != inGrow) && TrackBox (whichWindow, theEvent.where, thePart))) {
  871.                 GetPort (&oldPort);
  872.                 SetPort (whichWindow);
  873.                 EraseRect (&whichWindow->portRect);
  874.                 if (thePart == inGrow) SizeWindow (whichWindow, LoWord (newSize), HiWord (newSize), -1);
  875.                 else ZoomWindow (whichWindow, thePart, 0);
  876.                 commandWinResized = true;
  877.                 InvalRect (&whichWindow->portRect);
  878.                 SetPort (oldPort);
  879.             }
  880.             break;
  881.         }
  882.     }
  883. }
  884.  
  885. static void DoKeyPress (void) {
  886.     short whatKey = theEvent.message & charCodeMask;
  887.     if (theEvent.modifiers & cmdKey) {
  888.         long choice;
  889.         AdjustMenus ();
  890.         if (choice = MenuKey (theEvent.message)) DoMenu (choice);
  891.         else if (((whatKey == 'w') || (whatKey == 'W')) && (FrontWindow () == graphicsWin))
  892.             HideGrafWin ();
  893.         else if (whatKey == LEFTARROW)    GoStartOfLine ();
  894.         else if (whatKey == RIGHTARROW)    GoEndOfLine ();
  895.         else if (whatKey == UPARROW)    DoScrollBar (vScroll, -linesInView);
  896.         else if (whatKey == DOWNARROW)    DoScrollBar (vScroll, linesInView);
  897.     }
  898.     else if (theEvent.modifiers & optionKey) {
  899.         if (whatKey == LEFTARROW)        GoBackOneWord ();
  900.         else if (whatKey == RIGHTARROW)    GoForwardOneWord ();
  901.     }
  902.     else switch (whatKey) {
  903.         case PAGEUP:    DoScrollBar (vScroll, -linesInView);    break;
  904.         case PAGEDN:    DoScrollBar (vScroll, linesInView);        break;
  905.         case HOMEKEY:    DoScrollBar (vScroll, INT_MIN);            break;
  906.         case ENDKEY:    DoScrollBar (vScroll, INT_MAX);            break;
  907.         case FNKEY:                                                break;
  908.         case HELPKEY:                                            break;
  909.         default: recentChar = theEvent.message & charCodeMask;
  910.     }
  911. }
  912.  
  913. void DoEvent (void) {
  914.     if (wneImplemented) WaitNextEvent (everyEvent, &theEvent, NIL, NIL);
  915.     else {
  916.         SystemTask ();
  917.         GetNextEvent (everyEvent, &theEvent);
  918.     }
  919.  
  920.     AdjustCursor ();
  921.     if ((flashTime) && (--flashTime == 0)) SetSelection (cursorBeforeFlash, cursorBeforeFlash);
  922.     if (outputBufferLength) FlushOutput ();
  923.     if (FrontWindow () == commandWin) TEIdle (hTERec);
  924.     recentChar = '\0';
  925.  
  926.     if (howManyFiles) {
  927.         extern xlload (char *, int, int);
  928.         AppFile aFile;
  929.         GetAppFiles (++whichFile, &aFile);
  930.         if (whichFile == howManyFiles) {
  931.             howManyFiles = 0;
  932.             whichFile = 0;
  933.         }
  934.         SetVol (NIL, aFile.vRefNum);
  935.         PtoCstr (aFile.fName);
  936.         if (xlload ((char *) aFile.fName, 1, 0) == 0) xlabort ("load error");
  937.         ClrAppFiles (whichFile);
  938.         if (howManyFiles == 0) {
  939.             macputs ("> ");
  940.             PrepareForInput ();
  941.         }
  942.     }
  943.  
  944.     switch (theEvent.what) {
  945.         case mouseDown:
  946.             DoMouseDown ();
  947.             break;
  948.         case keyDown:
  949.         case autoKey:
  950.             DoKeyPress ();
  951.             break;
  952.         case activateEvt: {
  953.             WindowPtr whichWindow = (WindowPtr)theEvent.message;
  954.             SetPort (whichWindow);
  955.             if (whichWindow == commandWin) {
  956.                 DrawOnlyGrowIcon (commandWin);
  957.                 if ((theEvent.modifiers & activeFlag) == 1) {
  958.                     TEActivate (hTERec);
  959.                     HiliteControl (vScroll, 0);
  960.                 } else {
  961.                     TEDeactivate (hTERec);
  962.                     HiliteControl (vScroll, 255);
  963.                 }
  964.             }
  965.             break;
  966.         }
  967.         case updateEvt: {
  968.             if ((WindowPtr)theEvent.message == commandWin) UpdateCmdWindow ();
  969.             break;
  970.         }
  971.     }
  972. }
  973.  
  974. void MacWrapUp (void) {
  975.     /* take everything down in preparation for quitting */
  976.     StopPasting ();
  977.     CloseWindow (graphicsWin);
  978.     CloseWindow (commandWin);
  979.     TEDispose (hTERec);
  980.     DisposHandle (hOutputBuffer);
  981. }
  982.  
  983.